#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/stat.h>

#include "cJSON.h"
#include "curl/curl.h"

#include "cicd_common.h"
#include "compile_and_post_manager.h"
#include "message_queue.h"
#include "cicd_config.h"
#include "file_queue.h"

extern char global_output_repo[50];
extern char global_access_token[35];
extern char global_owner[50];

// 文件名称组装规则： 仓库名/'年-月-日 时-分-秒'/上传物

// curl回调函数
static size_t wirte_callback(void *contents, size_t size, size_t nmemb, void *userdata) {
    FILE *fp = userdata;
    size_t write_bytes = fwrite(contents, size, nmemb, fp);
    return write_bytes;
}

// 空格换成'%20'
static void replace_str_to_post(char *str) {
    int len = strlen(str);
    char temp[256] = { 0 };
    int i, counter = 0;
    for(i = 0; i < len; i++) {
        if(str[i] == ' ') {
            temp[counter] = '%';
            temp[counter + 1] = '2';
            temp[counter + 2] = '0';
            counter += 3;
        }
        else {
            temp[counter] = str[i];
            counter++;
        }
    }
    temp[counter] = '\0';
    strcpy(str, temp);
    str[counter] = '\0';
}

// 文件最大75MB
static char data[1024 * 1024 * 100] = { 0 };
// 获得base64格式的文件内容，返回内部申请的存储空间，用后需要释放
static char *get_file_base64(const char *file) {
    FILE *fp = NULL;
    int len = 0;

    memset(data, 0, sizeof(data));
    fp = fopen(file, "r");
    if(fp == NULL) {
        return NULL;
    }

    len = fread(data, sizeof(char), sizeof(data), fp);
    data[len] = '\0';

    fclose(fp);
    return base64_encode(data, len);
}

// 调用API获得Blob SHA，用后需要释放内存
static char *get_sha(const char token[], const char owner[], const char repo[], const char file[]) {
    CURL *curl = NULL;
    struct curl_slist *headers = NULL;
    char url[512] = { 0 };
    FILE *fp = NULL;
    char olen = 0;
    char *sha = NULL;
    char *str_tmp = NULL;

    cJSON *root = NULL;
    cJSON *json_sha = NULL;

    snprintf(url, sizeof(url), "%s/%s/%s/contents/%s?access_token=%s", global_https_header, owner, repo, file, token);
    headers = curl_slist_append(headers, global_request_header);

    printf("\033[32murl: %s\n\033[0m", url);

    fp = fopen("/tmp/curl_stream.txt", "w");

    curl = curl_easy_init();
    curl_easy_setopt(curl, CURLOPT_URL, url);
    curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, GET);
    curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
    curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, wirte_callback);
    curl_easy_setopt(curl, CURLOPT_WRITEDATA, fp);
    CURLcode ret = curl_easy_perform(curl);
    fclose(fp);

    if(ret != CURLE_OK) {
        printf_warning("Curl ret is %d\n", ret);
        sha = NULL;
        goto exit;
    }

    fp = fopen("/tmp/curl_stream.txt", "r");
    memset(data, 0, sizeof(data));
    olen = fread(data, sizeof(char), sizeof(data), fp);
    fclose(fp);

    sha = (char *)malloc(sizeof(char) * 50);

    root = cJSON_Parse(data);
    json_sha = cJSON_GetObjectItem(root, "sha");
    if(NULL == json_sha) {
        printf_error("json sha is NULL");
        free(sha);
        sha = NULL;
        goto exit;
    }
    str_tmp = cJSON_Print(json_sha);
    strncpy(sha, str_tmp, 50);
    free(str_tmp);

exit:

    curl_slist_free_all(headers);
    curl_easy_cleanup(curl);
    cJSON_Delete(root);

    return sha;
}

/**
 * @brief 上传文件
 * 
 * @param token \
 * @param owner \
 * @param repo \
 * @param branch \
 * @param file_header 文件头，每次上传需要上传两个文件，编译结果文件和产物文件,compile.txt upload.tar.gz
 * @param file_name 两次调用该接口，第一次传入"compile.txt"，第二次传入"upload.tar.gz"
 * @return int 
 */
static int upload_file(char token[], char owner[], char repo[], char branch[], char *file_header, char *file_name) {
    char url[512] = { 0 };
    char file[256] = { 0 };
    char *post_data = NULL;
    char post_file_name[256] = { 0 };
    cJSON *root = NULL, *access_token = NULL, *content = NULL, *message = NULL;
    char *json_string = NULL;
    char *sha = NULL;
    CURL *curl = NULL;
    struct curl_slist *headers = NULL;
    int ret = 1;

    // 拼接字符串
    snprintf(file, sizeof(file), "%s/%s", file_header, file_name);

    // 处理字文件名中的字符串
    replace_str_to_post(file);

    // 拼接url
    snprintf(url, sizeof(url), "%s/%s/%s/contents/%s", global_https_header, owner, global_output_repo, file);

    // 拼凑上传文件的路径，用来拿到数据
    snprintf(post_file_name, sizeof(post_file_name), "/tmp/%s", file_name);

    // 拿到文件的base64编码后的数据
    post_data = get_file_base64(post_file_name);
    if(NULL == post_data) {
        goto exit;
    }

    // JSON拼接
    root = cJSON_CreateObject();
    access_token = cJSON_CreateString(global_access_token);
    content = cJSON_CreateString(post_data);
    message = cJSON_CreateString("daily push");

    // JSON连接到root
    cJSON_AddItemToObject(root, "access_token", access_token);
    cJSON_AddItemToObject(root, "content", content);
    cJSON_AddItemToObject(root, "message", message);

    // 以字符串形式输出
    json_string = cJSON_Print(root);

    // 生成headers
    headers = curl_slist_append(headers, global_request_header);

    // curl进行post
    curl = curl_easy_init();
    curl_easy_setopt(curl, CURLOPT_URL, url);
    curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, POST);
    curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
    curl_easy_setopt(curl, CURLOPT_POSTFIELDS, json_string);
    curl_easy_perform(curl);

    sha = get_sha(global_access_token, global_owner, global_output_repo, file);
    if(sha == NULL) {
        printf_warning("can not get sha from json, url is %s\n", url);
        goto exit;
    }

    printf_info("post info: %s, %s, %s, %s, %s\n", global_access_token, global_owner, global_output_repo, file, sha);
    queue_file_add(global_access_token, global_owner, global_output_repo, file, sha);

    printf_success("File: %s uploaded successfully\n", file);

    ret = 0;

exit:
    curl_slist_free_all(headers);
    curl_easy_cleanup(curl);
    cJSON_Delete(root);
    if(NULL != post_data) {
        free(post_data);
    }
    if(json_string != NULL) {
        free(json_string);
    }
    if(sha != NULL) {
        free(sha);
    }
    return ret;
}

// 上传文件
static int file_upload(char token[], char owner[], char repo[], char branch[], char branch_hash[]) {
    char file_header[512] = { 0 };
    time_t current_time;
    struct tm *time_info;
    char formated_time[20] = { 0 };
    char branch_name[50] = { 0 };
    struct stat st;
    FILE *fp = NULL;

    // 按照规则拼接file_header
    current_time = time(NULL);
    time_info = localtime((const time_t *)&current_time);
    strftime(formated_time, sizeof(formated_time), "%Y-%m-%d %H-%M-%S", time_info);
    strncpy(branch_name, branch, sizeof(branch_name));
    delete_char(branch_name, strlen(branch), '\"');
    snprintf(file_header, sizeof(file_header),"%s/%s-%s-%s", repo, formated_time, branch_name, branch_hash);

    printf_info("start upload file compile.txt\n");
    fp = fopen("/tmp/compile.txt", "r");
    if(NULL == fp) {
        printf_error("/tmp/compile.txt does not exists\n");
    }
    else {
        if(upload_file(token, owner, repo, branch, file_header, "compile.txt") != 0) {
            printf_error("upload /tmp/compile.txt error\n");
        }
        fclose(fp);
    }

    printf_info("start upload file upload.tar.gz\n");
    fp = fopen("/tmp/upload.tar.gz", "r");
    if(NULL == fp) {
        printf_info("/tmp/upload.tar.gz does not exists\n");
    }
    else {
        if(upload_file(token, owner, repo, branch, file_header, "upload.tar.gz") != 0) {
            printf_error("upload /tmp/upload.tar.gz error\n");
        }
        fclose(fp);
    }
    return 0;
}

/*
流程如下：
1. 询问消息队列有无要编译的文件
2. 如果有，则进行编译
    2.1 组装命令下拉代码
    2.2 删除/tmp/compile.txt
    2.3 运行项目中的build.sh并重定向到/tmp/compile.txt
3. 调用内部接口进行上传
*/
void compile_and_post_once() {
    char command[1024] = { 0 };
    char token[128], owner[128], repo[128], branch[128], branch_hash[128];
    int ret = queue_compile_get(token, owner, repo, branch, branch_hash);

    if(ret != QUEUE_OK) {
        printf_debug("no compile\n");
        return;
    }

    printf_debug("compile\n");

    snprintf(command, sizeof(command), "rm -rf %s", repo);
    ret = system(command);

    memset(command, 0, sizeof(command));
    snprintf(command, sizeof(command), "git clone --recurse-submodules git@gitee.com:%s/%s.git", owner, repo);
    ret = system(command);

    memset(command, 0, sizeof(command));
    snprintf(command, sizeof(command), "cd %s && git checkout %s && git submodule init && git submodule update\n", repo, branch);
    ret = system(command);

    ret = system("rm /tmp/compile.txt");

    memset(command, 0, sizeof(command));
    snprintf(command, sizeof(command), "cd %s && ./build.sh > /tmp/compile.txt 2>&1", repo);
    ret = system(command);

    file_upload(token, owner, repo, branch, branch_hash);

    memset(command, 0, sizeof(command));
    snprintf(command, sizeof(command), "rm -rf %s", repo);
    ret = system(command);
}

